package cn.test.my.excel.poi;


import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import cn.test.my.excel.poi.annotation.Ignore;
import cn.test.my.excel.poi.annotation.SetCellStyle;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * excel 导出
 *
 * @author jiema
 * @since 2020/5/27 14:19
 */
@SuppressWarnings("unused")
public class PoiExcelExport<T> {
	private static final Logger log = LoggerFactory.getLogger(PoiExcelExport.class);


	/**
	 * 头名称
	 */
	private String[] headers;

	/**
	 * 列宽度
	 */
	private int[] headersLengths;

	/**
	 * 小数点位数
	 */
	private String[] scales;

	private int rowNumber = 0;

	private int sheetNumber = 1;

	private int index = 0;

	/**
	 * 标题
	 */
	private String title;

	/**
	 * 输出
	 */
	private OutputStream out;

	/**
	 * 日期格式
	 */
	private String[] datePatterns;

	/**
	 * 声明一个工作薄
	 */
	private HSSFWorkbook workbook;

	/**
	 * 声明一个表格
	 */
	private HSSFSheet sheet;


	/**
	 * 声明每一列的样式
	 */
	private CellStyle[] styles;

	/**
	 * 声明一个字体
	 */
	private Font font;

	/**
	 * 字段排序
	 */
	private List<Entry<Field, Integer>> fieldSort;

	private void initWorkbook(int sheetNum, String sheetTitle) {
		// 生成一个表格
		sheet = workbook.createSheet();

		workbook.setSheetName(sheetNum, sheetTitle);
	}

	@SuppressWarnings("unused")
	public PoiExcelExport(String title, OutputStream out, Class<?> clazz) {
		// 初始化一个工作薄
		workbook = new HSSFWorkbook();
		// workbook.isWriteProtected();
		// workbook.writeProtectWorkbook("123", "");

		initWorkbook(0, title + "-0");
		this.title = title;
		this.out = out;
		//初始化样式
		initRowStyle(clazz);
	}

	private void initRowStyle(Class<?> clazz) {
		Map<Field, Integer> map = new LinkedHashMap<>();
		Field[] fields = clazz.getDeclaredFields();

		// 筛选带注解字段
		for (Field value : fields) {
			if (value.isAnnotationPresent(Ignore.class)) {
				continue;
			}
			if (value.isAnnotationPresent(SetCellStyle.class)) {
				SetCellStyle col = value.getAnnotation(SetCellStyle.class);
				map.put(value, col.sortNum());
			}
		}
		Comparator<Entry<Field, Integer>> comp = Comparator.comparingInt(Entry::getValue);
		fieldSort = new ArrayList<>(map.entrySet());
		fieldSort.sort(comp);

		this.headers = new String[fieldSort.size()];
		this.headersLengths = new int[fieldSort.size()];
		this.scales = new String[fieldSort.size()];
		this.styles = new CellStyle[fieldSort.size()];
		this.datePatterns = new String[fieldSort.size()];
		// 排序后,初始化样式
		for (int i = 0, b = fieldSort.size(); i < b; i++) {
			Entry<Field, Integer> entry = fieldSort.get(i);
			Field field = entry.getKey();
			if (field.isAnnotationPresent(SetCellStyle.class)) {
				SetCellStyle col = field.getAnnotation(SetCellStyle.class);
				headers[i] = col.columnName();
				headersLengths[i] = col.columnLength();
				scales[i] = "".equals(col.numberScale()) ? null : col.numberScale();
				styles[i] = setCommCellStyle();
				datePatterns[i] = col.datePattern();
			}
		}
	}

	private CellStyle setCommCellStyle() {
		// 生成一个样式
		CellStyle style = workbook.createCellStyle();

		// 设置这些样式
		style.setFillForegroundColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());
		style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
		style.setBorderBottom(BorderStyle.THIN);
		style.setBorderLeft(BorderStyle.THIN);
		style.setBorderRight(BorderStyle.THIN);
		style.setBorderTop(BorderStyle.THIN);
		style.setAlignment(HorizontalAlignment.CENTER);
		// 生成一个字体
		font = workbook.createFont();
		font.setFontHeightInPoints((short) 11);
		font.setBold(false);
		// 把字体应用到当前的样式
		style.setFont(font);
		return style;
	}


	@SuppressWarnings("WeakerAccess")
	public void beginExport() {
		// 产生表格标题行
		HSSFRow row = sheet.createRow(0);
		for (short i = 0; i < headers.length; i++) {
			HSSFCell cell = row.createCell(i);
			sheet.setColumnWidth(i, headersLengths[i]);
			cell.setCellStyle(styles[i]);
			HSSFRichTextString text = new HSSFRichTextString(headers[i]);
			cell.setCellValue(text);
		}
	}

	@SuppressWarnings("unused")
	public void endExport() {
		try {
			workbook.write(out);
		}
		catch (IOException e) {
			log.error("endExport ERROR", e);
		}
	}

	@SuppressWarnings("unused")
	public void write(Collection<T> items) {
		int invalidRowNumber = 60000;
		if (rowNumber != 0 && rowNumber % invalidRowNumber == 0) {
			initWorkbook(sheetNumber, title + "-" + sheetNumber);
			beginExport();
			sheetNumber++;
			index = 0;
		}

		HSSFRow row;
		// 遍历集合数据，产生数据行
		for (T item : items) {
			rowNumber++;
			index++;
			row = sheet.createRow(index);
			// 利用反射，根据javabean属性的先后顺序，动态调用getXxx()方法得到属性值
			int fieldsLen = fieldSort.size();
			// 使用一个cellIdx为了对应数组这种不定长度
			short cellIdx = 0, i;

			for (i = 0; i < fieldsLen; i++, cellIdx++) {
				HSSFCell cell = row.createCell(cellIdx);
				cell.setCellStyle(styles[i]);
				String fieldName = fieldSort.get(i).getKey().getName();
				String getMethodName =
						"get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
				try {
					Class<?> tCls = item.getClass();
					Method getMethod = tCls.getMethod(getMethodName);
					Object value = getMethod.invoke(item);
					// 判断值的类型后进行强制类型转换
					String textValue = null;
					if (value instanceof Date) {
						Date date = (Date) value;
						SimpleDateFormat sdf = new SimpleDateFormat(
								Optional.ofNullable(datePatterns[i]).orElse(PoiConstants.DATE_PATTERN));
						textValue = sdf.format(date);
					}
					else if (value instanceof LocalDate) {
						LocalDate val = (LocalDate) value;
						textValue = PoiConstants.DATE_TIME_FORMAT.format(val);
					}
					else if (value instanceof LocalTime) {
						LocalTime val = (LocalTime) value;
						textValue = PoiConstants.TIME_FORMAT.format(val);
					}
					else if (value instanceof LocalDateTime) {
						LocalDateTime val = (LocalDateTime) value;
						textValue = PoiConstants.DATE_TIME_FORMAT.format(val);
					}
					// else if (value instanceof BaseEnum) {
					//     textValue = ((BaseEnum) value).getName();
					// }
					else {
						if (value != null) {
							textValue = value.toString();
						}
					}
					if (textValue != null && !"".equals(textValue)) {
						if (null != scales[i]) {
							HSSFDataFormat format = workbook.createDataFormat();
							styles[i].setDataFormat(format.getFormat(scales[i]));
							cell.setCellValue(Double.parseDouble(textValue));
						}
						else {
							HSSFRichTextString richString = new HSSFRichTextString(textValue);
							richString.applyFont(font);
							cell.setCellValue(richString);
						}
					}
				}
				catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
					log.error("write ERROR", e);
				}
			}
		}
	}
}

